home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / xlib / bufferogl.c < prev    next >
C/C++ Source or Header  |  1996-11-11  |  20KB  |  554 lines

  1. /*
  2.  * Copyright (c) 1993-94, Silicon Graphics, Inc.
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software and
  5.  * its documentation for any purpose is hereby granted without fee, provided
  6.  * that the name of Silicon Graphics may not be used in any advertising or
  7.  * publicity relating to the software without the specific, prior written
  8.  * permission of Silicon Graphics.
  9.  *
  10.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
  11.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
  12.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  13.  *
  14.  * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
  15.  * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
  16.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE
  17.  * POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
  18.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  19.  *
  20.  * OpenGL(TM) is a trademark of Silicon Graphics, Inc.
  21.  */
  22. /*
  23.  * bufferogl:  an openGL-Xlib version of Yusuf Attarwala's Motif-based 4.0
  24.  *             mixed model "bufferglm.c" demo program.  This is the "after"
  25.  *             version, ported from its "before" counterpart, located at
  26.  *             ../../GLX/buffer/bufferglx.c
  27.  *             
  28.  *             bufferogl implements an openGL methodology for creating and
  29.  *             then switching back-and-forth between single and double 
  30.  *             -buffer visuals or drawing modes.
  31.  * 
  32.  *       left mouse  :  switch to single buffer
  33.  *       middle mouse:  switch to double buffer
  34.  *       right mouse :  animate the green sphere in the current buffer mode
  35.  *       ESC key     :  exits the program
  36.  *
  37.  *       the program handles redraw (ConfigureNotify--move and resize) and
  38.  *       expose (pop, expose, de-iconify) and button (i.e. mouse) events.
  39.  *                                      ratmandu -- ported July, 1993   
  40.  */
  41. #include <stdio.h>
  42. #include <stdlib.h>
  43. #include <GL/glx.h>
  44. #include <GL/gl.h>
  45. #include <GL/glu.h>
  46. #include <X11/Xlib.h>
  47. #include <X11/Xutil.h>
  48. #include <X11/keysym.h>
  49.  
  50. #define top     0                            /* parent X window array index */
  51. #define SBglwin 1                    /* singlebuffer'd RGBA GL window index */
  52. #define DBglwin 2                    /* doublebuffer'd RGBA GL window index */
  53. #define WINMAX  3                               /* number of unique windows */
  54.  
  55. #define Mat1 1                       /* we'll use two material definitions, */
  56. #define Mat2 2                       /* for lighting, one for each sphere   */
  57.  
  58. static void openwindow(char *);
  59. void Winset(int);
  60. static void resize_buffer(long);
  61. static void clean_exit(void);
  62. void initGL(void);
  63. void setMatrix(void);
  64. void loop(void);
  65. void drawScene(void);
  66.  
  67. Display *dpy;                         /* The X server connection            */
  68. Atom del_atom;                        /* for possibility of WM killing app  */
  69. Window glwins[WINMAX];                /* array for SB/DB window ids         */
  70. GLXContext glcontexts[WINMAX];        /* array for SB/DB window context ids */
  71. float ax, ay, az;                     /* angles green sphere twirls on      */
  72. int xsize, ysize;                     /* current size-of-window keepers     */
  73. long buffermode;                      /* flag for cur win (single||double)  */
  74. XEvent event;                         /* needs to be global for XIfEvent    */
  75. GLUquadricObj *quadObj;               /* used to make the two spheres with  */
  76.  
  77.  
  78. main(argc,argv)
  79. int argc;
  80. char **argv;
  81. {
  82.     KeySym keysym;                              /* for catching             */
  83.     char buf[4];                                /* keyboard events          */
  84.     int myExpose, myConfigure, 
  85.         myButtRelease, myKeyPress;              /* store which events occur */
  86.  
  87.  
  88.     myExpose = myConfigure = myButtRelease = myKeyPress = GL_FALSE;
  89.  
  90.     openwindow(argv[0]);
  91.    
  92.    /* initialize *both* windows just as we wud do with winopen/winset */
  93.     Winset(DBglwin);
  94.     initGL();                                          /* do GL init stuff */
  95.     setMatrix();
  96.   
  97.    /* start out making the singlebuffer window be our current GL window */
  98.     Winset(SBglwin);
  99.     initGL();                                          /* do GL init stuff */
  100.     setMatrix();
  101.     drawScene();    /* and begin by drawing the scene in singlebuffer mode */
  102.  
  103.  
  104.     /*
  105.      * The event loop.
  106.      */
  107.     while (1) {         /* standard logic:  get event(s), process event(s) */
  108.  
  109.         glFlush();                        /* For proper DGL performance */
  110.  
  111.     /* this "do while" loop does the `get events' half of the "get events,
  112.      *  process events" action of the infinite while.  this is to ensure
  113.      *  the event queue is always drained before the events that have come
  114.      *  in are processed.
  115.      */
  116.         do {
  117.  
  118.             XNextEvent(dpy, &event);
  119.                 switch (event.type) {
  120.  
  121.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  122.              *  terms of when a window becomes visible, or a previously
  123.              *  invisible part becomes visible.
  124.              */
  125.                 case Expose:                                   /* Exposures */
  126.                     myExpose = GL_TRUE;
  127.                     break;
  128.  
  129.  
  130.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  131.              *   to a window's size or position.
  132.              */
  133.                 case ConfigureNotify:                 /* Resize GL manually */
  134.                     if (event.xconfigure.window == glwins[top]) {
  135.             /* save the changed width/height of the parent X window */
  136.                         xsize = event.xconfigure.width;
  137.                         ysize = event.xconfigure.height;
  138.                         myConfigure = GL_TRUE;
  139.                     } 
  140.                     break;
  141.  
  142.  
  143.             /* Wait for "ButtonRelease" events so the queue doesn't fill up
  144.              *  the way it wud if the user sits on ButtonPresss.
  145.              */
  146.                 case ButtonRelease:                        
  147.                     if (event.xbutton.button == Button1) {   /* LEFTMOUSE:  */
  148.                         buffermode = SBglwin;                /* switch to   */
  149.                         myButtRelease = GL_TRUE;             /* singlebuffer*/
  150.                     } else if (event.xbutton.button == Button2) {
  151.                         buffermode = DBglwin;       /* MIDDLEMOUSE:  switch */
  152.                         myButtRelease = GL_TRUE;    /* to doublebuffer mode */
  153.                     } else if (event.xbutton.button == Button3) {
  154.                         loop();                             /* RIGHTMOUSE:  */
  155.                     }                                 /* twirl green sphere */
  156.                     break;
  157.  
  158.  
  159.             /* "ClientMessage" is generated if the WM itself is being
  160.              *  gunned down and sends an exit signal to any running prog.
  161.              */
  162.                 case ClientMessage:
  163.                     if (event.xclient.data.l[0] == del_atom)
  164.                         clean_exit();
  165.                     break;
  166.  
  167.  
  168.             /* "KeyPress" events are those that would be generated before
  169.              *   whenever queueing up any KEYBD key via qdevice.
  170.              */
  171.                 case KeyPress:
  172.                    /* save out which unmodified key (i.e. the  key was
  173.                     *  not modified w/something like "Shift", "Ctrl",
  174.                     *  or "Alt") got pressed for use below.
  175.                     */
  176.                     XLookupString((XKeyEvent *)&event, buf, 4, &keysym, 0);
  177.                     myKeyPress = GL_TRUE;
  178.                     break;
  179.  
  180.               }  /* end switch (event.type) */
  181.  
  182.  
  183.         } while (XPending(dpy));   /* end "do { } while".
  184.                                     * XPending() is like qtest()--it only
  185.                                     * tells you if there're any events
  186.                                     * presently in the queue.  it does not
  187.                                     * disturb queue's contents in any way.
  188.                                     */
  189.  
  190.     /* On an "Expose" event, redraw the affected pop'd or de-iconized window
  191.      */
  192.         if (myExpose) {
  193.             drawScene();                               /* draw the GL stuff */
  194.             myExpose = GL_FALSE;             /* reset flag--queue now empty */
  195.         }
  196.  
  197.     /* On a "ConfigureNotify" event, the GL window has either been moved or
  198.      *  resized.  Respond accordingly and then redraw its contents.
  199.      */
  200.         if (myConfigure) {
  201.             if (buffermode == SBglwin) {
  202.                 resize_buffer(DBglwin);      /* update viewport & CTM stuff */
  203.                 resize_buffer(SBglwin);      /* for *both* windows and then */
  204.             } else if (buffermode == DBglwin) {
  205.                 resize_buffer(SBglwin);
  206.                 resize_buffer(DBglwin);
  207.             } 
  208.             drawScene();
  209.             myConfigure = GL_FALSE;          /* reset flag--queue now empty */
  210.         }
  211.  
  212.     /* On a "ButtonRelease" event, the flag gets set only for the LEFTMORE
  213.      *  or MIDDLEMOUSE.  we need to change the buffermode state in this case.
  214.      *  In either case, XStoreName updates the window's title bar, Winset
  215.      *  then switches to the current buffermode graphics context, and we
  216.      *  Map the current window *BEFORE* we unmap the previous one.  this
  217.      *  alleviates a painful "flicker" that occurs if you unmap the previous
  218.      *  window first.
  219.      */
  220.         if (myButtRelease) {
  221.             if (buffermode == SBglwin) {
  222.                 XStoreName(dpy,glwins[top],
  223.                    "single[LEFTMOUSE]-buffer GL window");
  224.         XRaiseWindow(dpy, glwins[SBglwin]);
  225.                 Winset(SBglwin);
  226.             } else if (buffermode == DBglwin) {
  227.                 XStoreName(dpy,glwins[top],
  228.                    "double[RIGHTMOUSE]-buffer GL window");
  229.         XRaiseWindow(dpy, glwins[DBglwin]);
  230.                 Winset(DBglwin);
  231.             }
  232.             myButtRelease = GL_FALSE;
  233.         }
  234.  
  235.         /* On a keypress of Esc key, exit program.
  236.          */
  237.         if (myKeyPress) {
  238.             if (keysym == XK_Escape)
  239.                 clean_exit();
  240.         }
  241.  
  242.     }      /* end while(1) */
  243.  
  244. }     /* end main */
  245.  
  246.  
  247.  
  248. static int SBattributeList[] = { GLX_RGBA, 
  249.                  GLX_DEPTH_SIZE, 1, 
  250.                  None };
  251. static int DBattributeList[] = { GLX_RGBA, 
  252.                  GLX_DOUBLEBUFFER, 
  253.                  GLX_DEPTH_SIZE, 1, 
  254.                  None };
  255.  
  256. /* WaitForNotify:
  257.  *   used to make sure the MapWindow() calls inside openwindow() occur
  258.  *   beFORE glXMakeCurrent() is invoked so as to avoid a race condition.
  259.  */
  260. static Bool WaitForNotify(Display *d, XEvent *e, char *arg) {
  261.     return (e->type == MapNotify) && (e->xmap.window == (Window)arg);
  262. }
  263.  
  264.  
  265. /*  openwindow -
  266.  *     establish connection to X server, get screen info, specify the
  267.  *     attributes we want the WM to try to provide, and create the 2
  268.  *     GL windows--one single and one double -buffered--that we'll employ.
  269.  */
  270. static void openwindow(char *progname) {
  271.  
  272.     XSizeHints Winhints;                          /* used to fix window size */
  273.     XSetWindowAttributes SBswa, DBswa;
  274.     XColor gray;
  275.     XVisualInfo *SBvi, *DBvi;
  276.     Colormap SBcmap, DBcmap;
  277.     int scrnnum;                              /* X screen number            */
  278.     int xorig, yorig;
  279.     long scrnheight;                          /* for defining maximum resize*/
  280.  
  281.  
  282.  
  283.   /* Connect to the X server and get screen info */
  284.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  285.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  286.                                  progname, XDisplayName(NULL));
  287.         exit(1);
  288.     }
  289.     scrnnum = DefaultScreen(dpy);
  290.     scrnheight = DisplayHeight(dpy, scrnnum);
  291.  
  292.     xorig = 0;  yorig = 0;                    /* define window initial size */
  293.     xsize = 300; ysize = 300;
  294.  
  295.    /* first create the top level X window which will be the parent to both
  296.     * the singlebuffer and doublebuffer rendering windows
  297.     */
  298.     glwins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnnum),
  299.                       xorig, yorig, xsize, ysize, 0, 0, 0);
  300.     if (!(glwins[top])) {
  301.         fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
  302.     exit(1);
  303.     }
  304.    /* including this call to XSelectInput shud not be necessary, but there's
  305.     * a bug, so we have to resort to this hackaround.
  306.     */
  307.     XSelectInput(dpy, glwins[top],
  308.           StructureNotifyMask|ButtonPressMask|ButtonReleaseMask|KeyPressMask);
  309.  
  310.    /* define the string that will show up in the window title bar (and icon) */
  311.     XStoreName(dpy,glwins[top],"single/double -buffer program");
  312.  
  313.    /* specify the values for the Window Size Hints we want to enforce:  this
  314.     *  window's aspect ratio needs to stay at 1:1, constrain min and max
  315.     *  window size, and specify the initial size of the window.
  316.     */
  317.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  318.     Winhints.height = ysize;
  319.     Winhints.min_width = xsize;                       /* define min and max */
  320.     Winhints.max_width = scrnheight-1;                /* width and height   */
  321.     Winhints.min_height = ysize;
  322.     Winhints.max_height = scrnheight-1;
  323.     Winhints.min_aspect.x = 1;                /* keep aspect at a 1:1 ratio */
  324.     Winhints.max_aspect.x = 1;
  325.     Winhints.min_aspect.y = 1;
  326.     Winhints.max_aspect.y = 1;
  327.                                              /* set the corresponding flags */
  328.     Winhints.flags = USSize|PMaxSize|PMinSize|PAspect;
  329.     XSetNormalHints(dpy, glwins[top], &Winhints);
  330.  
  331.  
  332.    /* now define and create the single and doublebuffer rendering windows */
  333.  
  334.   /* first get the appropriate visuals */
  335.     SBvi = glXChooseVisual(dpy, scrnnum, SBattributeList);
  336.     DBvi = glXChooseVisual(dpy, scrnnum, DBattributeList);
  337.  
  338.   /* create the appropriate  GLX contexts */
  339.     glcontexts[SBglwin] = glXCreateContext(dpy, SBvi, 0, GL_TRUE);
  340.     glcontexts[DBglwin] = glXCreateContext(dpy, DBvi, 0, GL_TRUE);
  341.  
  342.   /* now create the two colormaps */
  343.     SBcmap = XCreateColormap(dpy, RootWindow(dpy, SBvi->screen),
  344.                            SBvi->visual, AllocNone);
  345.     DBcmap = XCreateColormap(dpy, RootWindow(dpy, DBvi->screen),
  346.                            DBvi->visual, AllocNone);
  347.  
  348.   /* now create both windows */
  349.     SBswa.colormap = SBcmap;
  350.     SBswa.border_pixel = 0;
  351.     SBswa.event_mask = StructureNotifyMask | ButtonReleaseMask | 
  352.        ButtonPressMask | KeyPressMask;    /* express interest in events */
  353.     glwins[SBglwin] = XCreateWindow(dpy, glwins[top],
  354.                       xorig, yorig, xsize, ysize,
  355.                       0, SBvi->depth, InputOutput, SBvi->visual,
  356.                       CWBorderPixel|CWColormap|CWEventMask, &SBswa);
  357.     DBswa.colormap = DBcmap;
  358.     DBswa.border_pixel = 0;
  359.     DBswa.event_mask = StructureNotifyMask  | ButtonReleaseMask | 
  360.        ButtonPressMask | KeyPressMask;    /* express interest in events */
  361.     glwins[DBglwin] = XCreateWindow(dpy, glwins[top],
  362.                       xorig, yorig, xsize, ysize,
  363.                       0, DBvi->depth, InputOutput, DBvi->visual,
  364.                       CWBorderPixel|CWColormap|CWEventMask, &DBswa);
  365.  
  366.   /* now map both windows, but leave the singlebuffer window on top */
  367.     XMapWindow(dpy, glwins[SBglwin]);
  368.     XMapWindow(dpy, glwins[DBglwin]);
  369.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[DBglwin]);
  370.     XRaiseWindow(dpy, glwins[SBglwin]);
  371.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[SBglwin]);
  372.     XMapWindow(dpy, glwins[top]);
  373.  
  374.   /* connect the context to the window */
  375.     glXMakeCurrent(dpy, glwins[SBglwin], glcontexts[SBglwin]);
  376.  
  377.   /* ensure the GL colormap is installed for this app */
  378.     XSetWMColormapWindows(dpy, glwins[top], glwins, WINMAX);
  379.  
  380.   /* express interest in WM being able to kill this app */
  381.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  382.         XSetWMProtocols(dpy, glwins[top], &del_atom, 1);
  383.  
  384.     glFlush();
  385. }
  386.  
  387.  
  388. char *typeToName[] = {
  389.     "parent window",
  390.     "RGB single buffer",
  391.     "RGB double buffer",
  392. };
  393.  
  394.  
  395. /* A little helper wrapper for glXMakeCurrent.
  396.  *  passes the index to jointly access the windows and contexts array and
  397.  *  checks the return value.  This makes the call to begin GL drawing a
  398.  *  little simpler.  Building in such automatic error checking is always a
  399.  *  "smooth move" (*not* like the cancerously-mutant human with the enlarged
  400.  *  proboscis plastered all over the place urging people to be likewise 
  401.  *  smoothly cancerous and hardly cool).
  402.  */
  403. void Winset(int type)
  404. {
  405.     int rv;
  406.  
  407.     /*
  408.     XIfEvent(dpy, &event, WaitForNotify, (char*)glwins[type]);
  409.     */
  410.     XSync(dpy,GL_FALSE);
  411.     rv = glXMakeCurrent(dpy, glwins[type], glcontexts[type]);
  412.     if (rv == GL_FALSE) {
  413.         fprintf(stderr, "glXMakeCurrent failed for an %s-type window\n", 
  414.                                             typeToName[type]);
  415.         exit(-1);
  416.     }
  417. }
  418.  
  419.  
  420. /*  window has been moved or resized so update viewport & CTM stuff.
  421.  */
  422. static void resize_buffer(long wintype) {
  423.  
  424.     Winset(wintype);
  425.     XMoveResizeWindow(dpy, glwins[wintype], 0, 0, xsize, ysize);
  426.     glViewport(0, 0, xsize-1, ysize-1);
  427. }
  428.  
  429.  
  430.  
  431. /*  clean up before exiting
  432.  */
  433. static void clean_exit(void)
  434. {
  435.     XCloseDisplay(dpy);
  436.     exit(0);
  437. }
  438.  
  439.  
  440. /* setup all necessary GL initialzation parameters.
  441.  *  do our materials/lighting definitions, enable what we need, and define
  442.  *  the properites with gluQuadric for our two sphere objects.
  443.  */
  444. void
  445. initGL()
  446. {
  447.     GLfloat mat1_diff[] = { 0.6, 0.2, 0.2, 1.0 };
  448.     GLfloat mat1_spec[] = { 1.0, 1.0, 1.0, 1.0 };
  449.     GLfloat mat1_shin[] = { 120.0 };
  450.  
  451.     GLfloat mat2_diff[] = { 0.2, 0.8, 0.2, 1.0 };
  452.     GLfloat mat2_spec[] = { 1.0, 1.0, 1.0, 1.0 };
  453.     GLfloat mat2_shin[] = { 120.0 };
  454.  
  455.     GLfloat light_ambi[] =  { 0.2,  0.2,  0.2, 1.0 };
  456.     GLfloat light_diff[] =  { 0.9,  0.9,  0.9, 1.0 }; /* diffuse & specular */
  457.     GLfloat light_spec[] =  { 0.9,  0.9,  0.9, 1.0 }; /* are now equivalent */
  458.     GLfloat light_posi[] = { 10.0, 50.0, 50.0, 0.0 }; /* to the old LCOLOR  */
  459.  
  460.     GLfloat liteModel_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
  461.  
  462.     glNewList(Mat1, GL_COMPILE);
  463.         glMaterialfv(GL_FRONT,   GL_DIFFUSE, mat1_diff);
  464.         glMaterialfv(GL_FRONT,  GL_SPECULAR, mat1_spec);
  465.         glMaterialfv(GL_FRONT, GL_SHININESS, mat1_shin);
  466.     glEndList();
  467.  
  468.     glNewList(Mat2, GL_COMPILE);
  469.         glMaterialfv(GL_FRONT,   GL_DIFFUSE, mat2_diff);
  470.         glMaterialfv(GL_FRONT,  GL_SPECULAR, mat2_spec);
  471.         glMaterialfv(GL_FRONT, GL_SHININESS, mat2_shin);
  472.     glEndList();
  473.  
  474.     glLightfv(GL_LIGHT0,  GL_AMBIENT, light_ambi);
  475.     glLightfv(GL_LIGHT0,  GL_DIFFUSE, light_diff);
  476.     glLightfv(GL_LIGHT0, GL_SPECULAR, light_spec);
  477.     glLightfv(GL_LIGHT0, GL_POSITION, light_posi);
  478.     glLightModelfv (GL_LIGHT_MODEL_AMBIENT, liteModel_ambient); 
  479.  
  480.     glEnable(GL_LIGHTING);
  481.     glEnable(GL_LIGHT0);
  482.     glEnable(GL_DEPTH_TEST);
  483.  
  484.     quadObj = gluNewQuadric();            /* used to define the two spheres */
  485.     gluQuadricDrawStyle(quadObj,GLU_FILL);  /* draw the spheres as polygons */
  486.     gluQuadricNormals(quadObj, GLU_SMOOTH);      /* make the spheres smooth */
  487.     gluQuadricOrientation(quadObj,GLU_OUTSIDE);  /* draw w/nrmls pnting out */
  488.  
  489.     buffermode = SBglwin;                 /* start out in singlebuffer mode */
  490. }
  491.  
  492.  
  493. /* define our Current Transformation Matrix
  494.  */
  495. void
  496. setMatrix()
  497. {
  498.     glMatrixMode(GL_PROJECTION);
  499.     glLoadIdentity();
  500.     glOrtho(-10.0, 10.0, -10.0*ysize/xsize, 10.0*ysize/xsize, -10.0, 10.0);
  501.     glMatrixMode(GL_MODELVIEW);
  502.     glLoadIdentity();
  503.     gluLookAt(0.0, 0.0, 4.0, 0.0, -1.5, 0.0, 0.0, 1.0, 0.0);
  504. }
  505.  
  506.  
  507.  
  508. /* draw our two spheres
  509.  */
  510. void
  511. drawScene()
  512. {
  513.     glClearColor(0.3, 0.3, 0.3, 0.0);
  514.     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  515.  
  516.     glPushMatrix();
  517.       glPushMatrix();
  518.         glCallList(Mat1);
  519.     glTranslatef(0.2, 0.2, 0.2);
  520.         gluSphere(quadObj, 3.0, 20, 20);
  521.       glPopMatrix();
  522.         glRotatef(-ax, 1.0, 0.0, 0.0);
  523.         glRotatef(ay, 0.0, 1.0, 0.0);
  524.         glRotatef(az, 0.0, 0.0, 1.0);
  525.     glTranslatef(3.4, 3.4, 0.2);
  526.         glCallList(Mat2);
  527.         gluSphere(quadObj, 4.0, 20, 20);
  528.     glPopMatrix();
  529.  
  530.     if (buffermode == DBglwin) 
  531.         glXSwapBuffers(dpy, glwins[DBglwin]);
  532.  
  533.     glFlush();
  534. }
  535.  
  536.  
  537. /* perform one loop of twirling the green sphere
  538.  */
  539. void
  540. loop()
  541. {
  542.     register int i;
  543.  
  544.     for (i=0;i<50;i++) {
  545.         ax += 5.0;
  546.         ay -= 2.0;
  547.         az += 5.0;
  548.         if (ax >=  360.0)  ax = 0.0;
  549.         if (ay <= -360.0)  ay = 0.0;
  550.         if (az >=  360.0)  az = 0.0;
  551.         drawScene();
  552.     }
  553. }
  554.